#include "tcp_connection.h"
#include <boost/bind.hpp>

TcpConnection::TcpConnection(io_service& io_server)
:socket_(io_server), strand_(io_server), index_(0)
{
}

void TcpConnection::OnMessage(const boost::system::error_code& err,
	std::size_t bytes_transferred)
{
	if (!err)
	{
		LOG4CPLUS_TRACE(Log, "receive " << bytes_transferred << " bytes message");
		std::string buff(req_stream_.size(), '\0');
		std::istream is(&req_stream_);
		is.read(&buff[0], req_stream_.size());

		HandleMessage(buff);

		PrepareRead();
	}
	else
	{
		LOG4CPLUS_ERROR(Log, "response error: " << err.message());
		Disconnect();
	}
}

void TcpConnection::SendMessage(const std::string& message)
{
	{
		std::lock_guard<std::mutex> lock(mutex_);
		ostream os(&resp_stream_);
		os << message;
	}

	async_write(socket_, resp_stream_, strand_.wrap(
		boost::bind(&TcpConnection::OnWriteFinish, shared_from_this(),
		boost::asio::placeholders::error,
		boost::asio::placeholders::bytes_transferred)));
}

void TcpConnection::OnWriteFinish(const boost::system::error_code& err,
	std::size_t bytes_transferred)
{
	if (err)
	{
		LOG4CPLUS_ERROR(Log, "response error: " << err.message());
		Disconnect();
	}
	else
	{
		LOG4CPLUS_TRACE(Log, "send " << bytes_transferred << " bytes");
	}
}

void TcpConnection::PrepareRead()
{
	boost::asio::async_read(socket_, req_stream_,
		boost::asio::transfer_at_least(1), strand_.wrap(
		boost::bind(&TcpConnection::OnMessage, shared_from_this(),
		boost::asio::placeholders::error,
		boost::asio::placeholders::bytes_transferred)));
}

void TcpConnection::HandleMessage(const std::string& data)
{
	SendMessage(data);
}

void TcpConnection::Start()
{
	if (socket_.is_open())
	{
		boost::asio::socket_base::keep_alive option(true);
		socket_.set_option(option);
		tcp::no_delay noDelay(true);
		socket_.set_option(noDelay);
	}
	PrepareRead();
}

std::string TcpConnection::remote_addr()
{
	if (socket_.is_open())
		return socket_.remote_endpoint().address().to_string();
	
	return std::string();
}

void TcpConnection::Disconnect()
{
	if (socket_.is_open())
		socket_.close();
}